/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */
/*
 * main.c
 * Copyright (C) Jonas Hagmar 2009 <gnotterdev@gmail.com>
 * 
 * main.c is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * main.c is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <malloc.h>
#include <string.h>

#include <pthread.h>
#include <gtk/gtk.h>
#include <panel-applet.h>
#include <panel-applet-gconf.h>
#include <gconf/gconf-client.h>
#include "twitter_api.h"
#include "gtrstatustextview.h"
#include "gtrpixcache.h"
#include "gtractionbuttons.h"
#include "gtrshortenurl.h"

#define GTR_TYPE_APPLET		(gtr_applet_get_type ())
#define GTR_APPLET(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), GTR_TYPE_APPLET, GtrApplet))
#define GTR_APPLET_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GTR_TYPE_APPLET, GtrAppletClass))
#define GTR_IS_APPLET(o)	(G_TYPE_CHECK_INSTANCE_TYPE ((o), GTR_TYPE_APPLET))
#define GTR_IS_APPLET_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), GTR_TYPE_APPLET))
#define GTR_APPLET_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), GTR_TYPE_APPLET, GtrAppletClass))

static char const *KEY_USER_NAME = "user_name";
static char const *KEY_PASSWORD = "password";

static char const *ICON_GNOTTER = PIXMAPDIR "gnotter.svg";

typedef enum
{
  SET,
  REPLY,
  RETWEET
} StatusEntryState;

typedef struct
{
  PanelApplet parent;
  /* applet state */

  GtkWidget *status_window;
  GtkWidget *status_viewport;
  GtkWidget *status_table;
  GtkWidget *status_entry_tv;
  GtkWidget *status_send_button;
  GtkWidget *status_shorten_button;
  GtkWidget *status_count_label;
  gchar *status_entry_prefix;
  gchar *status_entry_id;
  StatusEntryState status_entry_state;

  GtkWidget *icon;

  pthread_t fetch_thread;
  pthread_mutex_t fetch_mutex;
  twapi_status_list new_statuses;
  gboolean fetch_started;

  pthread_mutex_t status_mutex;
  twapi_status_list statuses;

  PanelAppletOrient orient;

  guint timeout;

  GtkWidget *props;

  GtkWidget *user_name_entry;
  char *user_name;
  guint user_name_listener;

  GtkWidget *password_entry;
  char *password;
  guint password_listener;
} GtrApplet;

typedef struct
{
  PanelAppletClass parent_class;
} GtrAppletClass;

GType gtr_applet_get_type (void);

static void gtr_applet_class_init (GtrAppletClass * klass);
static void gtr_applet_init (GtrApplet * applet);

G_DEFINE_TYPE (GtrApplet, gtr_applet, PANEL_TYPE_APPLET)
     enum
     {
       COL_STATUS = 0,
       NUM_COLS
     };

     static gboolean non_empty_login_info (GtrApplet * applet)
{
  return ((g_strcmp0 (applet->user_name, "") != 0
	   && g_strcmp0 (applet->password, "") != 0));
}

static void
position_status_popup (GtrApplet * applet,
		       GtkWidget * icon, GtkWidget * window)
{
  int x, y;
  int w, h;
  int icon_w, icon_h;
  GtkRequisition req;
  GdkScreen *screen;
  int n, i;
  GdkRectangle monitor;
  gboolean found_monitor;
  GdkGravity gravity;

  gdk_window_get_origin (icon->window, &x, &y);

  gtk_widget_size_request (window, &req);
  w = req.width;
  h = req.height;
  icon_w = icon->allocation.width;
  icon_h = icon->allocation.height;
  screen = gtk_window_get_screen (GTK_WINDOW (window));

  n = gdk_screen_get_n_monitors (screen);
  for (i = 0; i < n; i++)
    {
      gdk_screen_get_monitor_geometry (screen, i, &monitor);
      if (x >= monitor.x && x <= monitor.x + monitor.width &&
	  y >= monitor.y && y <= monitor.y + monitor.height)
	{
	  found_monitor = TRUE;
	  break;
	}
    }

  if (!found_monitor)
    {
      monitor.x = 0;
      monitor.y = 0;
      monitor.width = gdk_screen_get_width (screen);
      monitor.height = gdk_screen_get_height (screen);
    }

  applet->orient = panel_applet_get_orient (PANEL_APPLET (applet));

  switch (applet->orient)
    {
    case PANEL_APPLET_ORIENT_RIGHT:
      x += icon_w;
      if ((y + h) > monitor.y + monitor.height)
	y -= (y + h) - (monitor.y + monitor.height);

      if ((y + h) > (monitor.height / 2))
	gravity = GDK_GRAVITY_SOUTH_WEST;
      else
	gravity = GDK_GRAVITY_NORTH_WEST;
      break;
    case PANEL_APPLET_ORIENT_LEFT:
      x -= w;
      if ((y + h) > monitor.y + monitor.height)
	y -= (y + h) - (monitor.y + monitor.height);

      if ((y + h) > (monitor.height / 2))
	gravity = GDK_GRAVITY_SOUTH_EAST;
      else
	gravity = GDK_GRAVITY_NORTH_EAST;
      break;
    case PANEL_APPLET_ORIENT_DOWN:
      y += icon_h;
      if ((x + w) > monitor.x + monitor.width)
	x -= (x + w) - (monitor.x + monitor.width);

      gravity = GDK_GRAVITY_NORTH_EAST;
      break;
    case PANEL_APPLET_ORIENT_UP:
      y -= h;
      if ((x + w) > monitor.x + monitor.width)
	x -= (x + w) - (monitor.x + monitor.width);

      gravity = GDK_GRAVITY_SOUTH_WEST;
      break;
    }

  gtk_window_move (GTK_WINDOW (window), x, y);
  gtk_window_set_gravity (GTK_WINDOW (window), gravity);
}

static void
gtr_applet_destroy_cb (GtkObject * object)
{
  GtrApplet *applet = GTR_APPLET (object);
  GConfClient *client;

  client = gconf_client_get_default ();

  if (applet->timeout)
    {
      g_source_remove (applet->timeout);
    }

  /* ensure no fetch in progress */
  pthread_mutex_lock (&applet->fetch_mutex);

  if (applet->new_statuses != NULL)
    {
      twapi_status_list_free (applet->new_statuses);
      applet->new_statuses = NULL;
    }

  pthread_mutex_unlock (&applet->fetch_mutex);

  pthread_mutex_lock (&applet->status_mutex);

  if (applet->statuses != NULL)
    {
      twapi_status_list_free (applet->statuses);
      applet->statuses = NULL;
    }

  pthread_mutex_unlock (&applet->status_mutex);

  if (applet->props != NULL)
    {
      gtk_widget_destroy (GTK_WIDGET (applet->props));
    }

  if (applet->status_window != NULL)
    {
      gtk_widget_destroy (GTK_WIDGET (applet->status_window));
    }

  if (applet->user_name != NULL)
    {
      g_free (applet->user_name);
    }

  if (applet->password != NULL)
    {
      g_free (applet->password);
    }

  if (applet->status_entry_prefix != NULL)
  {
    g_free (applet->status_entry_prefix);
  }

  if (applet->status_entry_id != NULL)
  {
    g_free (applet->status_entry_id);
  }

  gconf_client_notify_remove (client, applet->user_name_listener);
  gconf_client_notify_remove (client, applet->password_listener);

  g_object_unref (G_OBJECT (client));

  twapi_cleanup ();

}

static void
on_realize (GtkWidget *ab,
         gpointer   user_data)
{
  GdkWindow *window = gtk_widget_get_window (GTK_WIDGET (ab));
  GdkEventMask mask;
  mask = gdk_window_get_events (window);
  gdk_window_set_events (GDK_WINDOW (window), mask | GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK);
}

static gboolean
on_enter_notify (GtkWidget *widget,
              GdkEventCrossing *event,
              gpointer          user_data)
{
  GtrActionButtons *ab = GTR_ACTION_BUTTONS (user_data);

  gtr_action_buttons_reveal (ab);

  return FALSE;
}

static gboolean
on_leave_notify (GtkWidget *widget,
              GdkEventCrossing *event,
              gpointer          user_data)
{
  GtrActionButtons *ab = GTR_ACTION_BUTTONS (user_data);

  gtr_action_buttons_schedule_cloak (ab);

  return FALSE;
}

static void
on_reply (GtkWidget * widget, gpointer data)
{
  GtrActionButtons *ab = GTR_ACTION_BUTTONS (data);
  GtrApplet *applet = GTR_APPLET (gtr_action_buttons_get_data (ab));
  gchar *text;
  GtkTextBuffer *buffer;
  
  text = g_strdup_printf ("@%s ", gtr_action_buttons_get_user_screen_name (ab));

  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (applet->status_entry_tv));

  gtk_text_buffer_set_text (buffer, text, -1);

  if (applet->status_entry_prefix != NULL)
  {
    g_free (applet->status_entry_prefix);
  }
  applet->status_entry_prefix = text;

  if (applet->status_entry_id != NULL)
  {
    g_free (applet->status_entry_id);
  }
  applet->status_entry_id = g_strdup (gtr_action_buttons_get_id (ab));

  applet->status_entry_state = REPLY;
  gtk_button_set_label (GTK_BUTTON (applet->status_send_button), "R_eply");
  gtk_widget_grab_focus (applet->status_entry_tv);
}

static void
on_retweet (GtkWidget * widget, gpointer data)
{
  GtrActionButtons *ab = GTR_ACTION_BUTTONS (data);
  GtrApplet *applet = GTR_APPLET (gtr_action_buttons_get_data (ab));
  gchar *text;
  GtkTextBuffer *buffer;
  
  text = g_strdup_printf ("RT @%s", gtr_action_buttons_get_user_screen_name (ab));

  if (applet->status_entry_prefix != NULL)
  {
    g_free (applet->status_entry_prefix);
  }
  applet->status_entry_prefix = text;

  text = g_strdup_printf ("%s:%s", text, gtr_action_buttons_get_text (ab));

  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (applet->status_entry_tv));

  gtk_text_buffer_set_text (buffer, text, -1);

  g_free (text);
  text = NULL;

  if (applet->status_entry_id != NULL)
  {
    g_free (applet->status_entry_id);
  }
  applet->status_entry_id = g_strdup (gtr_action_buttons_get_id (ab));

  applet->status_entry_state = RETWEET;
  gtk_button_set_label (GTK_BUTTON (applet->status_send_button), "R_etweet");
  gtk_widget_grab_focus (applet->status_entry_tv);
}

static void
update_status_tree_view (GtrApplet * applet)
{
  twapi_status_list list;
  GtkWidget *status;
  GtkContainer *container;
  GtkWidget *table;
  GtkWidget *image;
  GdkPixbuf *pixbuf;
  GtkWidget *event_box;
  GtkWidget *alignment;
  GtkWidget *ab;
  GdkColor color;
  int n_rows;
  int row;

  container = GTK_CONTAINER (applet->status_viewport);

  if (container->has_focus_chain)
    gtk_container_unset_focus_chain (container);

  gtk_container_foreach (container, (GtkCallback) gtk_widget_destroy, NULL);

  pthread_mutex_lock (&applet->status_mutex);

  list = applet->statuses;

  n_rows = 0;
  while (list)
  {
    n_rows++;
    list = list->next;
  }

  table = gtk_table_new (n_rows,
                         3,
                         FALSE);

  list = applet->statuses;

  row = 0;
  while (list)
    {
      row++;

      event_box = gtk_event_box_new ();

      gdk_color_parse ("white", &color);
      gtk_widget_modify_bg (GTK_WIDGET (event_box), GTK_STATE_NORMAL, &color);

      gtk_table_attach( GTK_TABLE (table),
                       GTK_WIDGET (event_box),
                       0,
                       1,
                       row,
                       row+1,
                       GTK_FILL | GTK_EXPAND,
                       GTK_FILL | GTK_EXPAND,
                       3,
                       0);

      alignment = gtk_alignment_new (0.0, 0.0, 0.0, 0.0);

      gtk_container_add (GTK_CONTAINER (event_box), GTK_WIDGET (alignment));
      
      image = gtk_image_new ();
      pixbuf = gtr_pixcache_get_pixbuf (list->user_profile_image_url);
      gtk_image_set_from_pixbuf (GTK_IMAGE (image), pixbuf);
      gdk_pixbuf_unref (pixbuf);
      gtk_container_add (GTK_CONTAINER (alignment), GTK_WIDGET (image));
                       
      status = gtr_status_text_view_new ();

      gtr_status_text_view_set_data (GTR_STATUS_TEXT_VIEW (status),
				     list->user_screen_name,
				     list->text,
				     list->created_at,
				     list->source_href,
				     list->source_name, list->id);

      gtk_widget_set_size_request (status, 300, -1);

      gtk_table_attach_defaults( GTK_TABLE (table),
                       GTK_WIDGET (status),
                       1,
                       2,
                       row,
                       row+1);

      ab = gtr_action_buttons_new ();

      gtr_action_buttons_set_data (GTR_ACTION_BUTTONS (ab), (gpointer)applet);
      gtr_action_buttons_set_user_screen_name (GTR_ACTION_BUTTONS (ab), list->user_screen_name);
      gtr_action_buttons_set_id (GTR_ACTION_BUTTONS (ab), list->id);
      gtr_action_buttons_set_text (GTR_ACTION_BUTTONS (ab), list->text);
      if (strcmp (list->user_screen_name, applet->user_name) != 0)
      {
	gtr_action_buttons_set_mask (GTR_ACTION_BUTTONS (ab), GTR_AB_REPLY | GTR_AB_RETWEET);
      }

      gtk_table_attach_defaults( GTK_TABLE (table),
                                GTK_WIDGET (ab),
                                2,
                                3,
                                row,
                                row+1);

      g_signal_connect (ab, "reply", G_CALLBACK (on_reply), ab);
      g_signal_connect (ab, "retweet", G_CALLBACK (on_retweet), ab);

      g_signal_connect (event_box, "enter-notify-event", G_CALLBACK (on_enter_notify), ab);
      g_signal_connect (event_box, "leave-notify-event", G_CALLBACK (on_leave_notify), ab);
      g_signal_connect (event_box, "realize", G_CALLBACK (on_realize), NULL);
      
      g_signal_connect (status, "enter-notify-event", G_CALLBACK (on_enter_notify), ab);
      g_signal_connect (status, "leave-notify-event", G_CALLBACK (on_leave_notify), ab);
      g_signal_connect (status, "realize", G_CALLBACK (on_realize), NULL);

      list = list->next;
    }

  pthread_mutex_unlock (&applet->status_mutex);

  gtk_container_add (GTK_CONTAINER (container), table);
  applet->status_table = table;

  gtk_widget_show_all (applet->status_viewport);


}

static void *
fetch_friends_timeline (void *data)
{
  GtrApplet *applet = data;
  twapi_status_element_ptr e;

  pthread_mutex_lock (&applet->fetch_mutex);

  if (applet->new_statuses != NULL)
    {
      twapi_status_list_free (applet->new_statuses);
      applet->new_statuses = NULL;
    }

  if (non_empty_login_info (applet))
    twapi_friends_timeline (applet->user_name, applet->password,
			    &applet->new_statuses);

  e = applet->new_statuses;
  while (e != NULL)
  {
    gtr_pixcache_cache_pic (e->user_profile_image_url);
    e = e->next;
  }

  pthread_mutex_unlock (&applet->fetch_mutex);

  return NULL;

}

static int
handle_timeout (gpointer data)
{
  GtrApplet *applet = data;

  if (applet->fetch_started)
    {
      if (pthread_mutex_trylock (&applet->fetch_mutex) == 0)
	{
	  pthread_join (applet->fetch_thread, NULL);
	  pthread_mutex_lock (&applet->status_mutex);
	  if (applet->statuses != NULL)
	    {
	      twapi_status_list_free (applet->statuses);
	      applet->statuses = NULL;
	    }
	  applet->statuses = applet->new_statuses;
	  applet->new_statuses = NULL;
	  applet->fetch_started = FALSE;
	  pthread_mutex_unlock (&applet->status_mutex);
	  pthread_mutex_unlock (&applet->fetch_mutex);

	  if (applet->status_window != NULL)
	    {
	      update_status_tree_view (applet);
	    }

	  applet->timeout = g_timeout_add (120000, handle_timeout, applet);
	}
      else
	{
	  applet->timeout = g_timeout_add (1000, handle_timeout, applet);
	}
    }
  else
    {
      pthread_create (&applet->fetch_thread, NULL, fetch_friends_timeline,
		      (void *) applet);
      applet->timeout = g_timeout_add (1000, handle_timeout, applet);
      applet->fetch_started = TRUE;
    }

  return 0;
}

static void
set_status_cb (GtkButton * button, GtrApplet * applet)
{
  gchar *status;
  GtkTextBuffer *buffer;
  GtkTextIter start, end;

  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (applet->status_entry_tv));
  gtk_text_buffer_get_start_iter (buffer, &start);
  gtk_text_buffer_get_end_iter (buffer, &end);
  status = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
  if (non_empty_login_info (applet) && g_strcmp0 (status, "") != 0)
  {
    if (applet->status_entry_state == REPLY)
    {
      twapi_update (applet->user_name, applet->password, status, applet->status_entry_id);
    }
    else
    {
      twapi_update (applet->user_name, applet->password, status, NULL);
    }
  }
  g_free (status);
  status = NULL;
  gtk_text_buffer_set_text (buffer, "", -1);
  if (!applet->fetch_started)
    {
      g_source_remove (applet->timeout);
      handle_timeout (applet);
    }
}

static void
shorten_url_cb (GtkButton * button, GtrApplet * applet)
{
  gchar *url = NULL;
  gchar *short_url;
  gboolean have_selection;
  GtkTextIter start, end;
  GtkTextBuffer *buffer;
  gint response;
  GtkWidget *dialog;

  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (applet->status_entry_tv));

  have_selection = gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
  
  if (have_selection)
  {
    url = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
  }

  response = gtr_shorten_url (url, GTK_WINDOW (applet->status_window), &short_url);

  g_free (url);

  if (response == GTK_RESPONSE_OK)
  {
    if (short_url != NULL)
    {
      if (have_selection)
      {
	gtk_text_buffer_delete (buffer, &start, &end);
      }
      gtk_text_buffer_insert_at_cursor (buffer, short_url, -1);
      g_free (short_url);
    }
    else
    {
      dialog = gtk_message_dialog_new (GTK_WINDOW (applet->status_window),
                                       GTK_DIALOG_DESTROY_WITH_PARENT,
                                       GTK_MESSAGE_ERROR,
                                       GTK_BUTTONS_CLOSE,
                                       "Error shortening URL");
      gtk_dialog_run (GTK_DIALOG (dialog));
      gtk_widget_destroy (dialog);
    }
  }
  
}

static void
status_entry_changed_cb (GtkTextBuffer * buffer, GtrApplet * applet)
{
  gchar *status;
  size_t prefix_len;
  GtkTextIter start, end;
  glong chars_left;
  gchar *chars_left_str;

  gtk_text_buffer_get_start_iter (buffer, &start);
  gtk_text_buffer_get_end_iter (buffer, &end);
  status = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);

  if (((applet->status_entry_state == REPLY) 
      || (applet->status_entry_state == RETWEET))
      && (applet->status_entry_prefix != NULL))
  {
    prefix_len = strlen (applet->status_entry_prefix);

    if (strncmp (status, applet->status_entry_prefix, prefix_len) != 0)
    {

      if (applet->status_entry_prefix != NULL)
      {
	g_free (applet->status_entry_prefix);
	applet->status_entry_prefix = NULL;
      }

      if (applet->status_entry_id != NULL)
      {
	g_free (applet->status_entry_id);
	applet->status_entry_id = NULL;
      }

      applet->status_entry_state = SET;
      gtk_button_set_label (GTK_BUTTON (applet->status_send_button), "S_et");
    }
  
  }

  chars_left = 140 - g_utf8_strlen (status, -1);
  if (chars_left < 0)
  {
    chars_left_str = g_strdup_printf ("<span fgcolor=\"red\">%ld</span>", chars_left);
  }
  else
  {
    chars_left_str = g_strdup_printf ("<span fgcolor=\"black\">%ld</span>", chars_left);
  }
  gtk_label_set_markup (GTK_LABEL (applet->status_count_label), chars_left_str);

  g_free (chars_left_str);
  chars_left_str = NULL;

  g_free (status);
  status = NULL;
}

static void
create_status_popup (GtrApplet * applet)
{
  GtkWidget *window;
  GtkWidget *vbox;
  GtkWidget *vbox2;
  GtkWidget *hbox;
  GtkWidget *entry;
  GtkWidget *send_button;
  GtkWidget *shorten_button;
  GtkWidget *count_label;
  GdkColor color;

  GtkWidget *scrolled_window;
  GtkWidget *viewport;

  applet->status_window = window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
  gtk_widget_set_parent (window, GTK_WIDGET (applet));

  vbox = (GtkWidget *) GTK_BOX (gtk_vbox_new (FALSE, 6));
  gtk_container_add (GTK_CONTAINER (window), vbox);

  scrolled_window =
    (GtkWidget *) GTK_SCROLLED_WINDOW (gtk_scrolled_window_new (NULL, NULL));
  gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 6);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
				  GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);

  gtk_widget_set_size_request (scrolled_window, -1, 300);

  applet->status_viewport = viewport = gtk_viewport_new (NULL, NULL);

  applet->status_table = NULL;

  gdk_color_parse ("white", &color);
  gtk_widget_modify_bg (GTK_WIDGET (viewport), GTK_STATE_NORMAL, &color);
  gtk_widget_modify_fg (GTK_WIDGET (viewport), GTK_STATE_NORMAL, &color);

  gtk_container_add (GTK_CONTAINER
                           (scrolled_window), viewport);

  hbox = gtk_hbox_new (FALSE, 6);

  gtk_container_add (GTK_CONTAINER (vbox), hbox);

  applet->status_entry_tv = entry = gtk_text_view_new ();

  gtk_widget_set_size_request (entry, 300, -1);

  gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (entry), GTK_WRAP_WORD_CHAR);
  gtk_text_view_set_left_margin (GTK_TEXT_VIEW (entry), 5);

  g_signal_connect (gtk_text_view_get_buffer(GTK_TEXT_VIEW (entry)), 
                    "changed", G_CALLBACK (status_entry_changed_cb),
		    applet);

  gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 3);

  vbox2 = gtk_vbox_new (FALSE, 3);

  gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0);

  applet->status_send_button = send_button = gtk_button_new_with_mnemonic ("S_et");

  g_signal_connect (GTK_WIDGET (send_button), "clicked",
		    G_CALLBACK (set_status_cb), applet);

  gtk_box_pack_start (GTK_BOX (vbox2), send_button, FALSE, FALSE, 0);

  applet->status_shorten_button = shorten_button = gtk_button_new_with_mnemonic ("_Short");

  g_signal_connect (GTK_WIDGET (shorten_button), "clicked",
                    G_CALLBACK (shorten_url_cb), applet);

  gtk_box_pack_start (GTK_BOX (vbox2), shorten_button, FALSE, FALSE, 0);

  applet->status_count_label = count_label = gtk_label_new ("140");

  gtk_box_pack_start (GTK_BOX (vbox2), count_label, FALSE, FALSE, 0);

  update_status_tree_view (applet);
}

static gboolean
applet_button_press_cb (GtkWidget * event_box,
			GdkEventButton * event, GtrApplet * applet)
{
  if (event->button != 1)
    return FALSE;

  if (event->type == GDK_BUTTON_PRESS)
    {
      if (applet->status_window != NULL)
	{
	  gtk_widget_destroy (GTK_WIDGET (applet->status_window));
	  applet->status_window = NULL;
	}
      else
	{
	  create_status_popup (applet);
	  position_status_popup (applet, applet->icon, applet->status_window);
	  gtk_widget_show_all (GTK_WIDGET (applet->status_window));
	}

    }

  return TRUE;
}

static void
gtr_applet_class_init (GtrAppletClass * class)
{
  /* nothing to do here */
}

static void
user_name_entry_changed_cb (GtkEntry * entry, GtrApplet * applet)
{
  const char *user_name;

  user_name = gtk_entry_get_text (entry);
  panel_applet_gconf_set_string (PANEL_APPLET (applet),
				 KEY_USER_NAME, user_name, NULL);
}

static void
password_entry_changed_cb (GtkEntry * entry, GtrApplet * applet)
{
  const char *password;

  password = gtk_entry_get_text (entry);
  panel_applet_gconf_set_string (PANEL_APPLET (applet),
				 KEY_PASSWORD, password, NULL);
}

static void
properties_response_cb (GtkWidget * widget, int id, GtrApplet * applet)
{
  gtk_widget_destroy (widget);
  if (!applet->fetch_started)
    {
      g_source_remove (applet->timeout);
      handle_timeout (applet);
    }
}

static void
display_properties_dialog (BonoboUIComponent * uic,
			   gpointer a, const gchar * verbname)
{
  GtrApplet *applet = (GtrApplet *) a;
  GtkWidget *dialog;
  GtkWidget *vbox;
  GtkWidget *label;
  GtkWidget *entry;

  if (applet->props)
    {
      gtk_window_set_screen (GTK_WINDOW (applet->props),
			     gtk_widget_get_screen (GTK_WIDGET (applet)));
      gtk_window_present (GTK_WINDOW (applet->props));
      return;
    }

  applet->props = dialog =
    gtk_dialog_new_with_buttons ("Gnotter Preferences", NULL, 0,
				 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);

  gtk_widget_set_parent (GTK_WIDGET (dialog), GTK_WIDGET (applet));
  gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
  gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
  gtk_window_set_screen (GTK_WINDOW (dialog),
			 gtk_widget_get_screen (GTK_WIDGET (applet)));
  gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
  gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2);

  vbox = gtk_vbox_new (FALSE, 6);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox, FALSE, FALSE,
		      0);

  label = gtk_label_new_with_mnemonic ("User _name:");
  gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
  gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);

  applet->user_name_entry = entry = gtk_entry_new ();
  gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
  gtk_entry_set_text (GTK_ENTRY (entry), applet->user_name);
  g_signal_connect (entry, "changed", G_CALLBACK (user_name_entry_changed_cb),
		    applet);

  label = gtk_label_new_with_mnemonic ("_Password:");
  gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
  gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);

  applet->password_entry = entry = gtk_entry_new ();
  gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 0);
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
  gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE);
  gtk_entry_set_text (GTK_ENTRY (entry), applet->password);
  g_signal_connect (entry, "changed", G_CALLBACK (password_entry_changed_cb),
		    applet);

  g_signal_connect (applet->props, "destroy",
		    G_CALLBACK (gtk_widget_destroyed), &applet->props);
  g_signal_connect (applet->props, "response",
		    G_CALLBACK (properties_response_cb), applet);


  gtk_widget_show_all (dialog);
}

static void
user_name_changed (GConfClient * client,
		   guint cnxn_id, GConfEntry * entry, GtrApplet * applet)
{
  char const *value;

  if (!entry->value || entry->value->type != GCONF_VALUE_STRING)
    return;

  value = gconf_value_get_string (entry->value);

  g_free (applet->user_name);
  applet->user_name = g_strdup (value);
  if (applet->props != NULL)
    {
      gtk_entry_set_text (GTK_ENTRY (applet->user_name_entry),
			  applet->user_name);
    }
}

static void
password_changed (GConfClient * client,
		  guint cnxn_id, GConfEntry * entry, GtrApplet * applet)
{
  char const *value;

  if (!entry->value || entry->value->type != GCONF_VALUE_STRING)
    return;

  value = gconf_value_get_string (entry->value);

  g_free (applet->password);
  applet->password = g_strdup (value);
  if (applet->props != NULL)
    {
      gtk_entry_set_text (GTK_ENTRY (applet->password_entry),
			  applet->password);
    }
}

static void
setup_gconf (GtrApplet * applet)
{
  GConfClient *client;
  char *key;

  client = gconf_client_get_default ();

  key = panel_applet_gconf_get_full_key (PANEL_APPLET (applet),
					 KEY_USER_NAME);
  applet->user_name_listener = gconf_client_notify_add (client, key,
							(GConfClientNotifyFunc)
							user_name_changed,
							applet, NULL, NULL);

  g_free (key);

  key = panel_applet_gconf_get_full_key (PANEL_APPLET (applet), KEY_PASSWORD);
  applet->password_listener = gconf_client_notify_add (client, key,
						       (GConfClientNotifyFunc)
						       password_changed,
						       applet, NULL, NULL);

  g_free (key);

  g_object_unref (G_OBJECT (client));
}

static void
on_change_orient (GtrApplet * applet, PanelAppletOrient orient)
{
  applet->orient = orient;
}

static void
gtr_applet_init (GtrApplet * applet)
{

  applet->statuses = NULL;
  applet->new_statuses = NULL;
  applet->orient = panel_applet_get_orient (PANEL_APPLET (applet));
  applet->status_window = NULL;
  applet->props = NULL;
  applet->timeout = 0;
  pthread_mutex_init (&applet->fetch_mutex, NULL);
  pthread_mutex_init (&applet->status_mutex, NULL);
  applet->fetch_started = FALSE;
  applet->status_entry_prefix = NULL;
  applet->status_entry_id = NULL;
  applet->status_entry_state = SET;
}

static void
display_about_dialog (BonoboUIComponent * uic,
		      gpointer a, const gchar * cname)
{
  static const gchar *const authors[] = {
    "Jonas Hagmar <gnotterdev@gmail.com>",
    NULL
  };
  /*static const gchar *const documenters[] = {
     "Jonas Hagmar <gnotterdev@gmail.com>",
     NULL
     }; */
  static const gchar *const artists[] = {
    "Jonas Hagmar <gnotterdev@gmail.com>",
    NULL
  };

  gtk_show_about_dialog (NULL,
			 "version", VERSION,
			 "copyright", "\xC2\xA9 2009 Jonas Hagmar",
			 "comments", "This applet is a client for Twitter",
			 "authors", authors,
			 /*"documenters",   documenters, */
			 "artists", artists,
			 /*"translator-credits",    _("translator-credits"), */
			 "logo-icon-name", "gnotter", NULL);
}

static const char context_menu_xml[] =
  "<popup name=\"button3\">\n"
  "   <menuitem name=\"Properties Item\" "
  "             verb=\"GtrProperties\" "
  "           _label=\"_Preferences...\"\n"
  "          pixtype=\"stock\" "
  "          pixname=\"gtk-properties\"/>\n"
  "   <menuitem name=\"About Item\" "
  "             verb=\"GtrAbout\" "
  "           _label=\"_About...\"\n"
  "          pixtype=\"stock\" "
  "          pixname=\"gtk-about\"/>\n" "</popup>\n";

static const BonoboUIVerb menu_verbs[] = {
  BONOBO_UI_VERB ("GtrProperties", display_properties_dialog),
  BONOBO_UI_VERB ("GtrAbout", display_about_dialog),
  BONOBO_UI_VERB_END
};

static gboolean
applet_factory (PanelApplet * a, const gchar * iid, gpointer user_data)
{
  GtrApplet *applet = (GtrApplet *) a;
  GtkWidget *icon;
  GdkPixbuf *pixbuf;

  if (strcmp (iid, "OAFIID:gnotterApplet") != 0)
    return FALSE;

  g_signal_connect (G_OBJECT (applet), "destroy",
		    G_CALLBACK (gtr_applet_destroy_cb), NULL);

  twapi_init ();

  panel_applet_set_background_widget (PANEL_APPLET (applet), GTK_WIDGET (applet));

  pixbuf = gdk_pixbuf_new_from_file_at_size (ICON_GNOTTER, 24, 24, NULL);
  applet->icon = icon = gtk_image_new ();
  gtk_image_set_from_pixbuf (GTK_IMAGE (icon), pixbuf);
  gdk_pixbuf_unref (pixbuf);

  gtk_container_add (GTK_CONTAINER (applet), icon);

  g_signal_connect (G_OBJECT (applet),
		    "button_press_event",
		    G_CALLBACK (applet_button_press_cb), applet);

  g_signal_connect (G_OBJECT (applet),
		    "change_orient", G_CALLBACK (on_change_orient), applet);


  gtk_widget_show_all (GTK_WIDGET (applet));

  panel_applet_setup_menu (PANEL_APPLET (applet),
			   context_menu_xml, menu_verbs, applet);

  applet->user_name = panel_applet_gconf_get_string (PANEL_APPLET (applet),
						     KEY_USER_NAME, NULL);

  applet->password = panel_applet_gconf_get_string (PANEL_APPLET (applet),
						    KEY_PASSWORD, NULL);

  if (applet->user_name == NULL)
    applet->user_name = g_strdup ("");

  if (applet->password == NULL)
    applet->password = g_strdup ("");

  setup_gconf (applet);

  gtr_pixcache_init ();

  handle_timeout (applet);

  return TRUE;
}

PANEL_APPLET_BONOBO_FACTORY ("OAFIID:gnotterApplet_Factory",
			     GTR_TYPE_APPLET,
			     "Gnotter", "0", applet_factory, NULL);
